home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / machack / Hacks96 / FlyPaper.sit / Fly Paper / FlyPaper Source / App Sources / filter.c < prev    next >
Text File  |  1996-06-22  |  12KB  |  303 lines

  1. // File "filter.c" -
  2.  
  3. #include <Types.h>
  4. #include <Resources.h>
  5. #include <LowMem.h>
  6. #include <TextServices.h>
  7.  
  8. #include "FlyPaperApp.h"
  9. #include "filter.h"
  10. #include "floaters.h"
  11.  
  12. // ***********************************************************************************
  13. // Global Declarations 
  14.  
  15. extern GlobalsRec glob;
  16.  
  17. // ***********************************************************************************
  18. // ***********************************************************************************
  19.  
  20. /*
  21.     The Not-So-Simple FAT jGNEFilter 
  22.         Original Code by Matt Slot (fprefect@umich.edu), 6/2/95 
  23.             with help and criticism (lots!) from Ed Wynne (arwyn@umich.edu).
  24.     
  25.     Quick Intro
  26.         Since jGNEFilters are nasty things in 68k, and pretty much impossible 
  27.         in PPC, writing simple and cross-compiling handler code is also. To 
  28.         facilitate use of this, I have written some interface routines to hide
  29.         some of the complexity from the application programmer.
  30.         
  31.     /    Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData);
  32.     |        Installs a jGNEFilter which can properly call a callback from within
  33.     |        the application. The callback handles incoming events wither either 
  34.     |        68k or PPC code, and gets passed the data pointer you send from here.
  35.     |        Install() returns a pointer the jGNEFilter (which has been loaded from
  36.     |        a resource) installed in the system heap -- or NIL to indicate an error.
  37.  
  38.     /    Ptr ReleaseEventFilter(Ptr filterProc);
  39.     |        Pass in the pointer to the jGNEFilter, and this routine will disable 
  40.     |        the handling and try to dispose of its storage in the System Heap.
  41.     |        You *must* do this if your helper function is going to disappear when
  42.     |        the application closes. 
  43.     
  44.     /    Code Resource 'jGNE', #128: the jGNEFilter
  45.     |        Since there is no way to get the same routine to compile on both 68K
  46.     |        and PPC, I have compiled the jGNEFilter into an executable resource.
  47.     |        The real code of this resource has been compiled from the additional
  48.     |        source file you can find in the project folder. Note that the code simply  
  49.     |        calls the ProcPtr for the Helper function blindly... whether 68k or PPC. 
  50.     |        See below for more details as to how the filter is loaded, unloaded,
  51.     |        and called.
  52.  
  53.     /    void EventFilterHelper(EventRecord *theEvent, Ptr helperData);
  54.     |        This callback is the workhorse of the event filter. Once installed, this  
  55.     |        routine sees every event that gets harvested and has an opportunity to
  56.     |        modify the record before the front application gets to see it. Ideally
  57.     |        this function can do the necessary work itself or pass off the event info
  58.     |        to the home application.
  59.     |        WARNING: For 68k code, the routine will be called from the current app's
  60.     |        context (A5/Globals, Rsrc File, HeapZone). PPC code will have a valid RTOC
  61.     |        (Globals access) but not Rsrc File, HeapZone, etc.
  62.     
  63.  
  64.     How this all works:
  65.         
  66.         Since I didn't want to write the jGNEFilter in C (OK, I couldn't figure out
  67.         a good way), the code is carried around in a resource. Originally, I had 
  68.         saved the routine as a constant string that was StuffHex()'ed into the System 
  69.         Heap pointer... but I got so many questions and comments I decided to do it 
  70.         the normal way.
  71.         
  72.         The jGNEFilter keeps 3 pieces of data inline: the next filter in the chain,
  73.         a pointer to the helper routine, and some extra data to pass to the helper.
  74.         Most importantly, we may not be able to remove the filter from the calling
  75.         chain...  the architecture just doesn't permit it! If we are able to safely
  76.         pull the filter out, we do. Otherwise the next best solution is to keep a flag,
  77.         that we can clear when we want to disable the functionality -- in fact, we
  78.         set or clear the pointer to the Helper Proc as the flag.
  79.         
  80.         Finally, the helper function is the meat of our jGNEFilter; it does the work
  81.         of the active filter. In the case of a 68K helper, it is accessed via a 
  82.         simple ProcPtr. In the case of a PPC helper, the installer sets up a valid
  83.         RoutineDescriptor (in the System Heap with the jGNEFilter) to invoke a 
  84.         MixedMode switch between the 68K caller (filter) and PPC routine (helper).
  85.         Again, when releasing the filter the handler disposes the descriptor and 
  86.         clear the inline ProcPtr/flag, since the helper function will probably be
  87.         disappearing when the application quits.
  88.         
  89.         If you are picking out events to handle within your app, my suggestion is to
  90.         keep a secondary Queue of events in the System Heap -- remember, you must 
  91.         allocate new EventRecords (since the current event belongs to the calling
  92.         app) into the System Heap (you need a heap that both the current process and
  93.         your own process can access). Given these events, your main event loop can 
  94.         suck out clicks or keydowns for dispatching internally and safely within your
  95.         own context.
  96.         
  97.         Also, Text Service windows don't receive Activate or Update events... you 
  98.         must check for those manually within your own event loop and handle them.
  99.     
  100. */
  101.  
  102. // ***********************************************************************************
  103. // ***********************************************************************************
  104.  
  105. Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData) {
  106.     Handle filterRsrc;
  107.     Ptr filterProc, data;
  108.     
  109.     // Create a duplicate function in the System Heap (so its *alway* there) and
  110.     //   copy the data across. Note: even though it is technically a function, we
  111.     //   can still treat it as data safely until it has been installed and invoked.
  112.     filterRsrc = Get1Resource(kJGNEFilterResType, kJGNEFilterResID);
  113.     filterProc = NewPtrSys(GetHandleSize(filterRsrc));
  114.     // If either is nil, there is no need to free.. we are quitting anyway
  115.     if (! filterRsrc || ! filterProc) return(0); 
  116.     BlockMove(*filterRsrc, filterProc, GetHandleSize(filterRsrc));
  117.     ReleaseResource(filterRsrc);
  118.     
  119.     // Get and install the current filter as the next filter in the chain.
  120.     data = (Ptr) LMGetGNEFilter();
  121.     BlockMove(&data, filterProc + kNextFilterOffset, sizeof(data));
  122.  
  123.     // Get and install the Helper function to do the real work (and as a flag to
  124.     //   indicate we are in business and accepting events). Remember that if we
  125.     //   generating PPC code, it is necessary to establish a Routine Descriptor.
  126.     SetZone(SystemZone());
  127.     data = (Ptr) NewFilterHelperProc(helperProc);
  128.     if (! data) return(0);
  129.     BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
  130.     SetZone(ApplicZone());
  131.  
  132.     // If the caller wants to pass data to the jGNEFilter Helper function. This
  133.     //   pointer (or handle if desired) *must* be allocated in the System Heap
  134.     //   if you don't plan on releasing the Filter before quitting. If you plan
  135.     //   on releasing the filter, then either the App or Sys heap will suffice.
  136.     data = helperData;
  137.     BlockMove(&helperData, filterProc + kEventHelperDataOffset, sizeof(data));
  138.     
  139.     // Install us, we are ready to do some work!
  140.     LMSetGNEFilter((GNEFilterUPP) filterProc);
  141.     
  142.     return(filterProc);
  143.     }
  144.  
  145. // ***********************************************************************************
  146. // ***********************************************************************************
  147.  
  148. Ptr ReleaseEventFilter(Ptr filterProc) {
  149.     Ptr data;
  150.     
  151.     if (! filterProc) return(0);
  152.  
  153.     // Clear the Helper location as an indicator that we have closed up shop. The
  154.     //   filter itself may lingers in the System Heap until shutdown unless we can
  155.     //   find a way to extract it from the chain (see below). On the other hand, the 
  156.     //   filter has been written so that if the Helper function pointer is NIL, the 
  157.     //   filter will do nothing at all. Let's zero it out for that (hopeful) case.
  158.     BlockMove(filterProc + kEventHelperOffset, &data, sizeof(data));
  159.     if (data) DisposeRoutineDescriptor((UniversalProcPtr) data);
  160.     data = 0;
  161.     BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
  162.     
  163.  
  164. //    Damon Cokenias -- Ugly style is one thing, but attempting to remove a patch
  165. //    is another all together.  I will not use this code!
  166.  
  167. #if 0
  168.     // If the installed filterProc is the first one in the chain, then we should
  169.     //    be able remove it and replace it with the next one (the one we would
  170.     //    normally jump to). If we can dispose the filterProc buffer, then we can
  171.     //    can recover those 50 bytes that remain in the System Heap.
  172.     // Thanks to HoverBar's Guy Fullerton (hedgeboy@realm.net) for the suggestion.
  173.     if (filterProc == (Ptr) LMGetGNEFilter()) {
  174.         // Remove our filterProc from the chain.
  175.         BlockMove(filterProc + kNextFilterOffset, &data, sizeof(data));
  176.         LMSetGNEFilter((GNEFilterUPP) data);
  177.         
  178.         BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
  179.         DisposePtr(filterProc);
  180.         }
  181.       else {
  182.         // Grab the data that was passed when initialized or as set in the Helper
  183.         //   function. The caller can then deallocate it if desired or necessary.
  184.         
  185.         BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
  186.         }
  187.  
  188. #endif
  189.     
  190.     return(data);
  191.     }
  192.  
  193. // ***********************************************************************************
  194. // ***********************************************************************************
  195.  
  196. void EventFilterHelper(EventRecord *theEvent, Ptr helperData) {
  197.     Boolean fwdThisEvent = false, filterThisEvent = false;
  198.     long saveA5;
  199.     EvQEl *fwdEvent;
  200.     
  201.     // This only does something in 68K code. We now have access to globals,
  202.     //   which PPC get for free from CFM; however, we won't have access to 
  203.     //   our application's Resource file/chain or HeapZone. Be careful!
  204.     saveA5 = SetA5((long) helperData);
  205.     
  206.     // Check to see if the floaters should all be hidden, then hide or show any
  207.     //   window's as necessary. Note: Update events will be entered/handled in
  208.     //   our context, so let our app get the CPU to handle the event quickly
  209.     TestScreenSaver();
  210.     if (ShowHideFloater(0)) WakeUpProcess(&glob.myPSN);
  211.     
  212.     switch(theEvent->what) {
  213.         case nullEvent:
  214.             break;
  215.         case mouseDown: {
  216.             short thePart, index;
  217.             long anotherA5;
  218.             Boolean found;
  219.             WindowPtr whichWin;
  220.             FloaterQElemPtr floatQElem;
  221.             
  222.             // I have removed the PtInRgn() tests for the floaters, since the problem
  223.             //   with FindServiceWindow() was A5-related. The solution is to restore
  224.             //   the current app's native A5 temporarily. Thanks go to Ammon Skidkmore
  225.             //   (ammon@cs.byu.edu), as well as Andrew Thaler (athaler@umich.edu)
  226.             anotherA5 = SetCurrentA5();
  227.             thePart = FindServiceWindow(theEvent->where, &whichWin);
  228.             SetA5(anotherA5);
  229.  
  230.             // Walk our window list, and test to see if this floater belongs to us
  231.             //   before we try to forward an AppleGuide click to our program. :)
  232.             for(found = false, floatQElem = GetIndFloater(index = 1, false);
  233.                     floatQElem && !found; floatQElem = GetIndFloater(++index, false))
  234.                 if (whichWin == floatQElem->floatWindow) found = true;
  235.             if (! found) break;
  236.             
  237.             theEvent->message = thePart;
  238.             switch(thePart) {
  239.                 case inMenuBar:
  240.                 case inSysWindow:
  241.                     break;
  242.                 case inDrag:
  243.                 case inContent:
  244.                 case inGrow:
  245.                 case inGoAway:
  246.                 case inZoomIn:
  247.                 case inZoomOut:
  248.                     fwdThisEvent = true;
  249.                     filterThisEvent = true;
  250.                     break;
  251.                 }
  252.             }
  253.             break;
  254.         case keyDown:
  255.         case autoKey: {
  256.             char theKey, theChar;
  257.             
  258.             theChar = theEvent->message & charCodeMask;
  259.             theKey = (theEvent->message & keyCodeMask) >> 8;
  260.             
  261.             // Add your selected tests for key-shortcuts
  262.             if ((theEvent->modifiers & cmdKey) && (theKey == 0x35)) {
  263.     
  264.                 theEvent -> what = nullEvent;
  265.                 
  266.                 if (theEvent -> modifiers & shiftKey)
  267.                     gRunning = false;
  268.                     
  269.                 // Cmd-Escape is the sequence to temporarily hide floaters
  270.                 //   and a wakeup to handle any pending update events
  271.                 glob.hideFloats = (glob.hideFloats) ? false : true;
  272.                 if (ShowHideFloater(0)) WakeUpProcess(&glob.myPSN);
  273.  
  274.                 fwdThisEvent = filterThisEvent = false;
  275.                 }
  276.             }
  277.             break;
  278.         default:
  279.             break;
  280.         }
  281.     
  282.  
  283.     if (fwdThisEvent) {
  284.         // We have discovered that this event deserves full attention. Forward it to
  285.         //   our home application by Q'ing it up as a pointer in the System Heap
  286.         fwdEvent = (EvQEl *) NewPtrSys(sizeof(*fwdEvent));
  287.         fwdEvent->qLink = 0;
  288.         fwdEvent->qType = evType;
  289.         BlockMove(&theEvent->what, &fwdEvent->evtQWhat, sizeof(EventRecord));
  290.         Enqueue((QElem *) fwdEvent, &glob.forwardedEvents);
  291.         
  292.         // Let our app get the CPU to handle the event quickly
  293.         WakeUpProcess(&glob.myPSN);
  294.         }
  295.         
  296.     // Does intercepting the event mean no one else should have it?
  297.     if (filterThisEvent) theEvent->what = nullEvent;
  298.  
  299.     // Restore the (68K) context before leaving
  300.     SetA5(saveA5);
  301.     }
  302.  
  303.